package com.bitcoin.scheduler.service;

import com.bitcoin.scheduler.domain.BccPriceInfo;
import com.bitcoin.scheduler.domain.BccPriceInfoCrawl;
import com.bitcoin.scheduler.domain.BccPriceInfoDetail;
import com.bitcoin.scheduler.repository.Bcc24HourVolumeDao;
import com.bitcoin.scheduler.repository.BccPriceInfoCrawlDao;
import com.bitcoin.scheduler.repository.BccPriceInfoDao;
import com.bitcoin.scheduler.repository.BccPriceInfoDetailDao;
import com.github.dozermapper.core.DozerBeanMapperBuilder;
import com.github.dozermapper.core.Mapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.MessageFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

@Service
@Slf4j
public class BccPriceInfoService {
    @Getter
    private final BccPriceInfoDao priceInfoDao;
    @Getter
    private final BccPriceInfoCrawlDao priceInfoCrawlDao;
    @Getter
    private final BccPriceInfoDetailDao priceInfoDetailDao;
    @Getter
    private final Bcc24HourVolumeDao volumeDao;

    private final Mapper mapper = DozerBeanMapperBuilder.create()
        .withCustomConverter((source, target, aClass, aClass1) -> source != null ? source : target)
        .build();

    @Autowired
    public BccPriceInfoService(BccPriceInfoDao priceInfoDao, BccPriceInfoCrawlDao priceInfoCrawlDao, BccPriceInfoCrawlDao priceInfoCrawlDao1, BccPriceInfoDetailDao priceInfoDetailDao, Bcc24HourVolumeDao volumeDao) {
        this.priceInfoDao = priceInfoDao;
        this.priceInfoCrawlDao = priceInfoCrawlDao1;
        this.priceInfoDetailDao = priceInfoDetailDao;
        this.volumeDao = volumeDao;
    }

    /**
     * 最接近指定时间的价格
     *
     * @param selectedDate 指定时间
     * @return 最接近指定时间的价格列表
     */
    public List<BccPriceInfoCrawl> findNewestCrawl(Date selectedDate) {
        List<BccPriceInfoCrawl> result = Lists.newArrayList();
        //前后时间内所有价格信息 默认前后五分钟
        List<BccPriceInfoCrawl> crawlList = priceInfoCrawlDao.findNewestCrawl(DateFormatUtils.format(selectedDate, "yyyy/MM/dd HH:mm:ss"), 5);
        //去除重复的价格信息
        Map<String, BccPriceInfoCrawl> priceDateMap = Maps.newConcurrentMap();
        for (BccPriceInfoCrawl crawl : crawlList) {
            boolean toAdd = true;
            if (priceDateMap.containsKey(crawl.getBccId())) {
                //如果后面的价格最接近指定时间 用后面的价格信息替换前面的信息
                toAdd = Math.abs(selectedDate.getTime() - priceDateMap.get(crawl.getBccId()).getPriceTimestamp().getTime()) > Math.abs(selectedDate.getTime() - crawl.getPriceTimestamp().getTime());
            }
            if (toAdd) {
                if (priceDateMap.containsKey(crawl.getBccId())) {
                    result.remove(priceDateMap.get(crawl.getBccId()));
                }
                priceDateMap.put(crawl.getBccId(), crawl);
                result.add(crawl);
            }
        }
        return result;
    }

    public Set<String> syncPriceDetail(Date stepDate) throws ServiceException {
        return syncPriceDetail(stepDate, false);

    }

    public Set<String> syncPriceDetail(Date stepDate, boolean updateFinalPrice) throws ServiceException {
        long start = System.currentTimeMillis();
        log.info("同步价格数据开始 价格时间:{}", DateFormatUtils.format(stepDate, "yyyy/MM/dd HH:mm:ss"));

        Set<String> keySet = Sets.newConcurrentHashSet();
        List<BccPriceInfoCrawl> crawlList = findNewestCrawl(stepDate);
        if (CollectionUtils.isEmpty(crawlList)) {
            String message = MessageFormat.format("价格任务未取到价格数据 价格时间：{0}", stepDate);
            throw new ServiceException(message);
        }

        if (updateFinalPrice) {
            List<BccPriceInfo> priceInfoList = crawlList.stream().map((crawl -> {

                BccPriceInfo priceInfo =
                    getPriceInfoDao().findTop1ByBccIdOrderByPriceTimestampDesc(crawl.getBccId())
                        .orElse(new BccPriceInfo());
                String sourceId = priceInfo.getId();
                mapper.map(crawl, priceInfo);
                //还原ID
                priceInfo.setId(sourceId);
                return priceInfo;
            })).collect(Collectors.toList());
            getPriceInfoDao().saveAll(priceInfoList);
        }


        List<BccPriceInfoDetail> priceInfoDetailList = crawlList.stream().map((crawl -> {
            BccPriceInfoDetail detail = new BccPriceInfoDetail();
            mapper.map(crawl, detail);
            detail.setPriceTime(stepDate);
            detail.setCreateTime(new Date());
            setMaData(detail);
            keySet.add(detail.getBccId());
            return detail;
        })).collect(Collectors.toList());
        getPriceInfoDetailDao().saveAll(priceInfoDetailList);
        log.info("同步价格数据结束 价格时间:{} 耗时:{}ms", DateFormatUtils.format(stepDate, "yyyy/MM/dd HH:mm:ss"), System.currentTimeMillis() - start);
        return keySet;
    }

    /**
     * 设置MA数据
     */
    private void setMaData(BccPriceInfoDetail detail) {
        Date priceTime = detail.getPriceTime();
        Date beginDate = DateUtils.addDays(priceTime, -30);
        String time = DateFormatUtils.format(priceTime, "HH:mm:ss");
        List<BccPriceInfoDetail> detailList = getPriceInfoDetailDao().findByPriceTime(detail.getBccId(), beginDate, time);
        BigDecimal ma5 = BigDecimal.ZERO;
        BigDecimal ma10 = BigDecimal.ZERO;
        BigDecimal ma20 = BigDecimal.ZERO;
        BigDecimal ma30 = BigDecimal.ZERO;
        for (int i = 0; i < detailList.size(); i++) {
            BigDecimal price = BigDecimal.valueOf(NumberUtils.toDouble(detailList.get(i).getPriceCny(), 0));
            //累计
            if (i < 5) {
                ma5 = ma5.add(price);
            }
            if (i < 10) {
                ma10 = ma10.add(price);
            }
            if (i < 20) {
                ma20 = ma20.add(price);
            }
            if (i < 30) {
                ma30 = ma30.add(price);
            }

            //取平均值
            if (i > 0) {
                ma5 = ma5.divide(BigDecimal.valueOf(2), 4, RoundingMode.HALF_UP);
                ma10 = ma10.divide(BigDecimal.valueOf(2), 4, RoundingMode.HALF_UP);
                ma20 = ma20.divide(BigDecimal.valueOf(2), 4, RoundingMode.HALF_UP);
                ma30 = ma30.divide(BigDecimal.valueOf(2), 4, RoundingMode.HALF_UP);
            }
        }
        detail.setMa5Cny(ma5);
        detail.setMa10Cny(ma10);
        detail.setMa20Cny(ma20);
        detail.setMa30Cny(ma30);
    }
}
